package genesis.familytree.server.common;


import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import genesis.familytree.server.common.mapper.JsonMapper;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;

import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

@JsonSerialize(using = JsonResult.JsonResultSerializer.class)
public class JsonResult {

    private static final String KEY_STATUS = "code";

    private static final int KEY_STATUS_SUCCESS = 0;
    private static final int KEY_STATUS_ERROR = -1;
    private static final int KEY_STATUS_PARAM_ERROR = 1;
    private static final int KEY_STATUS_VERIFY_CODE_REQUIRED = 2;

    private static final String KEY_MESSAGE = "message";
    private static final String KEY_PARAM_ERROR_MESSAGE = "paramErrors";

    protected Map<String, Object> dataMap = new LinkedHashMap<String, Object>();

    private JsonResult(){}

    public static JsonResult create(){
        return new JsonResult().success();
    }

    public Object attr(String key) {
        return this.dataMap.get(key);
    }

    public JsonResult attr(String key, Object value){
        if(value instanceof JsonResult){
            value = ((JsonResult)value).dataMap;
        }
        this.dataMap.put(key, value);
        return this;
    }

    public JsonResult success(){
        this.dataMap.put(KEY_STATUS, KEY_STATUS_SUCCESS);
        return this;
    }

    public JsonResult success(String message){
        return success().message(message);
    }

    public JsonResult success(String key, Object value){
        return success().attr(key, value);
    }

    public JsonResult error(){
        return error(KEY_STATUS_ERROR).message("系统暂时无法作出响应，请稍后再试。");
    }

    public JsonResult error(BindingResult bindingResult){
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            paramError(fieldError.getField(),
                    fieldError.getDefaultMessage());
        }

        return this;
    }

    public JsonResult paramError(String paramName, String message) {
        Object obj = attr(KEY_PARAM_ERROR_MESSAGE);
        Map<String, Object> map;
        if (null == obj || !(obj instanceof Map)) {
            map = new HashMap<>();
            attr(KEY_PARAM_ERROR_MESSAGE, map);
        }else {
            map = (Map) obj;
        }

        map.put(paramName, message);
        return error(KEY_STATUS_PARAM_ERROR);
    }

    public JsonResult showCaptcha() {
        return error(KEY_STATUS_VERIFY_CODE_REQUIRED);
    }

    public JsonResult showVerifyCode(String message) {
        return showCaptcha().message(message);
    }

    public JsonResult error(int errorCode){
        this.dataMap.put(KEY_STATUS, errorCode);
        return this;
    }

    public JsonResult error(int errorCode, String message){
        return error(errorCode).message(message);
    }

    public JsonResult error(String message){
        return error().message(message);
    }

    public boolean isError() {
        Object obj = attr(KEY_STATUS);

        return obj != null && obj instanceof Number && ((Number) obj).intValue() != 0;
    }

    public JsonResult message(String message){
        return attr(KEY_MESSAGE, message);
    }

    public Map<String, Object> build(){
        return this.dataMap;
    }

    public String toJson(){
       return JsonMapper.getInstance().toJson(dataMap);
    }

    public static class JsonResultSerializer extends JsonSerializer<JsonResult> {

        @Override
        public void serialize(JsonResult value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
            gen.writeObject(value.dataMap);
        }

    }
}
